/**
 * Copyright (c) 2013-2022, Alibaba Group Holding Limited;
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * </p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.aliyun.polardbx.binlog.canal.binlog;

import com.aliyun.polardbx.binlog.canal.HandlerEvent;
import com.aliyun.polardbx.binlog.canal.binlog.event.FormatDescriptionLogEvent;
import com.aliyun.polardbx.binlog.canal.binlog.event.LogHeader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Binary log event definitions. This includes generic code common to all types of log events, as well as specific code
 * for each type of log event. - All numbers, whether they are 16-, 24-, 32-, or 64-bit numbers, are stored in little
 * endian, i.e., the least significant byte first, unless otherwise specified. representation of unsigned integers,
 * called Packed Integer. A Packed Integer has the capacity of storing up to 8-byte integers, while small integers still
 * can use 1, 3, or 4 bytes. The value of the first byte determines how to read the number, according to the following
 * table:
 * <table>
 * <caption>Format of Packed Integer</caption>
 * <tr>
 * <th>First byte</th>
 * <th>Format</th>
 * </tr>
 * <tr>
 * <td>0-250</td>
 * <td>The first byte is the number (in the range 0-250), and no more bytes are used.</td>
 * </tr>
 * <tr>
 * <td>252</td>
 * <td>Two more bytes are used. The number is in the range 251-0xffff.</td>
 * </tr>
 * <tr>
 * <td>253</td>
 * <td>Three more bytes are used. The number is in the range 0xffff-0xffffff.</td>
 * </tr>
 * <tr>
 * <td>254</td>
 * <td>Eight more bytes are used. The number is in the range 0xffffff-0xffffffffffffffff.</td>
 * </tr>
 * </table>
 * - Strings are stored in various formats. The format of each string is documented separately.
 *
 * @author Changyuan.lh
 * @version 1.0
 * @see mysql-5.1.60/sql/log_event.h
 */
public abstract class LogEvent implements HandlerEvent {

    /*
     * 3 is MySQL 4.x; 4 is MySQL 5.0.0. Compared to version 3, version 4 has: - a different Start_log_event, which
     * includes info about the binary log (sizes of headers); this info is included for better compatibility if the
     * master's MySQL version is different from the slave's. - all events have a unique ID (the triplet (server_id,
     * timestamp at server start, other) to be sure an event is not executed more than once in a multimaster setup,
     * example: M1 / \ v v M2 M3 \ / v v S if a query is run on M1, it will arrive twice on S, so we need that S
     * remembers the last unique ID it has processed, to compare and know if the event should be skipped or not. Example
     * of ID: we already have the server id (4 bytes), plus: timestamp_when_the_master_started (4 bytes), a counter (a
     * sequence number which increments every time we write an event to the binlog) (3 bytes). Q: how do we handle when
     * the counter is overflowed and restarts from 0 ? - Query and Load (Create or Execute) events may have a more
     * precise timestamp (with microseconds), number of matched/affected/warnings rows and fields of session variables:
     * SQL_MODE, FOREIGN_KEY_CHECKS, UNIQUE_CHECKS, SQL_AUTO_IS_NULL, the collations and charsets, the PASSWORD()
     * version (old/new/...).
     */
    public static final int BINLOG_VERSION = 4;

    /* Default 5.0 server version */
    public static final String SERVER_VERSION = "5.0";

    /**
     * Event header offsets; these point to places inside the fixed header.
     */
    public static final int EVENT_TYPE_OFFSET = 4;
    public static final int SERVER_ID_OFFSET = 5;
    public static final int EVENT_LEN_OFFSET = 9;
    public static final int LOG_POS_OFFSET = 13;
    public static final int FLAGS_OFFSET = 17;

    /* event-specific post-header sizes */
    // where 3.23, 4.x and 5.0 agree
    public static final int QUERY_HEADER_MINIMAL_LEN = (4 + 4 + 1 + 2);
    // where 5.0 differs: 2 for len of N-bytes vars.
    public static final int QUERY_HEADER_LEN = (QUERY_HEADER_MINIMAL_LEN + 2);

    /* Enumeration type for the different types of log events. */
    public static final int UNKNOWN_EVENT = 0;
    public static final int START_EVENT_V3 = 1;
    public static final int QUERY_EVENT = 2;
    public static final int STOP_EVENT = 3;
    public static final int ROTATE_EVENT = 4;
    public static final int INTVAR_EVENT = 5;
    public static final int LOAD_EVENT = 6;
    public static final int SLAVE_EVENT = 7;
    public static final int CREATE_FILE_EVENT = 8;
    public static final int APPEND_BLOCK_EVENT = 9;
    public static final int EXEC_LOAD_EVENT = 10;
    public static final int DELETE_FILE_EVENT = 11;

    /**
     * NEW_LOAD_EVENT is like LOAD_EVENT except that it has a longer sql_ex, allowing multibyte TERMINATED BY etc; both
     * types share the same class (Load_log_event)
     */
    public static final int NEW_LOAD_EVENT = 12;
    public static final int RAND_EVENT = 13;
    public static final int USER_VAR_EVENT = 14;
    public static final int FORMAT_DESCRIPTION_EVENT = 15;
    public static final int XID_EVENT = 16;
    public static final int BEGIN_LOAD_QUERY_EVENT = 17;
    public static final int EXECUTE_LOAD_QUERY_EVENT = 18;

    public static final int TABLE_MAP_EVENT = 19;

    /**
     * These event numbers were used for 5.1.0 to 5.1.15 and are therefore obsolete.
     */
    public static final int PRE_GA_WRITE_ROWS_EVENT = 20;
    public static final int PRE_GA_UPDATE_ROWS_EVENT = 21;
    public static final int PRE_GA_DELETE_ROWS_EVENT = 22;

    /**
     * These event numbers are used from 5.1.16 and forward
     */
    public static final int WRITE_ROWS_EVENT_V1 = 23;
    public static final int UPDATE_ROWS_EVENT_V1 = 24;
    public static final int DELETE_ROWS_EVENT_V1 = 25;

    /**
     * Something out of the ordinary happened on the master
     */
    public static final int INCIDENT_EVENT = 26;

    /**
     * Heartbeat event to be send by master at its idle time to ensure master's online status to slave
     */
    public static final int HEARTBEAT_LOG_EVENT = 27;

    /**
     * In some situations, it is necessary to send over ignorable data to the slave: data that a slave can handle in
     * case there is code for handling it, but which can be ignored if it is not recognized.
     */
    public static final int IGNORABLE_LOG_EVENT = 28;
    public static final int ROWS_QUERY_LOG_EVENT = 29;

    /**
     * Version 2 of the Row events
     */
    public static final int WRITE_ROWS_EVENT = 30;
    public static final int UPDATE_ROWS_EVENT = 31;
    public static final int DELETE_ROWS_EVENT = 32;

    public static final int GTID_LOG_EVENT = 33;
    public static final int ANONYMOUS_GTID_LOG_EVENT = 34;

    public static final int PREVIOUS_GTIDS_LOG_EVENT = 35;

    /* MySQL 5.7 events */
    public static final int TRANSACTION_CONTEXT_EVENT = 36;

    public static final int VIEW_CHANGE_EVENT = 37;

    /* Prepared XA transaction terminal event similar to Xid */
    public static final int XA_PREPARE_LOG_EVENT = 38;

    // mariaDb 5.5.34
    /* New MySQL/Sun events are to be added right above this comment */
    public static final int MYSQL_EVENTS_END = 39;

    /**
     * RDS TSO EVENT
     */
    public static final int SEQUENCE_EVENT = 80;

    /* DataNode 8.0 TSO EVENT */
    public static final int GCN_EVENT = 105;

    public static final int MARIA_EVENTS_BEGIN = 160;
    /* New Maria event numbers start from here */
    public static final int ANNOTATE_ROWS_EVENT = 160;
    /*
     * Binlog checkpoint event. Used for XA crash recovery on the master, not used in replication. A binlog checkpoint
     * event specifies a binlog file such that XA crash recovery can start from that file - and it is guaranteed to find
     * all XIDs that are prepared in storage engines but not yet committed.
     */
    public static final int BINLOG_CHECKPOINT_EVENT = 161;
    /*
     * Gtid event. For global transaction ID, used to start a new event group, instead of the old BEGIN query event, and
     * also to mark stand-alone events.
     */
    public static final int GTID_EVENT = 162;
    /*
     * Gtid list event. Logged at the start of every binlog, to record the current replication state. This consists of
     * the last GTID seen for each replication domain.
     */
    public static final int GTID_LIST_EVENT = 163;

    public static final int START_ENCRYPTION_EVENT = 164;

    /**
     * end marker
     */
    public static final int ENUM_END_EVENT = 165;

    /**
     * 1 byte length, 1 byte format Length is total length in bytes, including 2 byte header Length values 0 and 1 are
     * currently invalid and reserved.
     */
    public static final int EXTRA_ROW_INFO_LEN_OFFSET = 0;
    public static final int EXTRA_ROW_INFO_FORMAT_OFFSET = 1;
    public static final int EXTRA_ROW_INFO_HDR_BYTES = 2;
    public static final int EXTRA_ROW_INFO_MAX_PAYLOAD = (255 - EXTRA_ROW_INFO_HDR_BYTES);

    // Events are without checksum though its generator
    public static final int BINLOG_CHECKSUM_ALG_OFF = 0;
    // is checksum-capable New Master (NM).
    // CRC32 of zlib algorithm.
    public static final int BINLOG_CHECKSUM_ALG_CRC32 = 1;
    // the cut line: valid alg range is [1, 0x7f].
    public static final int BINLOG_CHECKSUM_ALG_ENUM_END = 2;
    // special value to tag undetermined yet checksum
    public static final int BINLOG_CHECKSUM_ALG_UNDEF = 255;
    // or events from checksum-unaware servers

    public static final int CHECKSUM_CRC32_SIGNATURE_LEN = 4;
    public static final int BINLOG_CHECKSUM_ALG_DESC_LEN = 1;
    /**
     * defined statically while there is just one alg implemented
     */
    public static final int BINLOG_CHECKSUM_LEN = CHECKSUM_CRC32_SIGNATURE_LEN;

    /* MySQL or old MariaDB slave with no announced capability. */
    public static final int MARIA_SLAVE_CAPABILITY_UNKNOWN = 0;

    /* MariaDB >= 5.3, which understands ANNOTATE_ROWS_EVENT. */
    public static final int MARIA_SLAVE_CAPABILITY_ANNOTATE = 1;
    /*
     * MariaDB >= 5.5. This version has the capability to tolerate events omitted from the binlog stream without
     * breaking replication (MySQL slaves fail because they mis-compute the offsets into the master's binlog).
     */
    public static final int MARIA_SLAVE_CAPABILITY_TOLERATE_HOLES = 2;
    /* MariaDB >= 10.0, which knows about binlog_checkpoint_log_event. */
    public static final int MARIA_SLAVE_CAPABILITY_BINLOG_CHECKPOINT = 3;
    /* MariaDB >= 10.0.1, which knows about global transaction id events. */
    public static final int MARIA_SLAVE_CAPABILITY_GTID = 4;

    /* Our capability. */
    public static final int MARIA_SLAVE_CAPABILITY_MINE = MARIA_SLAVE_CAPABILITY_GTID;

    /**
     * For an event, 'e', carrying a type code, that a slave, 's', does not recognize, 's' will check 'e' for
     * LOG_EVENT_IGNORABLE_F, and if the flag is set, then 'e' is ignored. Otherwise, 's' acknowledges that it has found
     * an unknown event in the relay log.
     */
    public static final int LOG_EVENT_IGNORABLE_F = 0x80;

    /**
     * enum_field_types
     */
    public static final int MYSQL_TYPE_DECIMAL = 0;
    public static final int MYSQL_TYPE_TINY = 1;
    public static final int MYSQL_TYPE_SHORT = 2;
    public static final int MYSQL_TYPE_LONG = 3;
    public static final int MYSQL_TYPE_FLOAT = 4;
    public static final int MYSQL_TYPE_DOUBLE = 5;
    public static final int MYSQL_TYPE_NULL = 6;
    public static final int MYSQL_TYPE_TIMESTAMP = 7;
    public static final int MYSQL_TYPE_LONGLONG = 8;
    public static final int MYSQL_TYPE_INT24 = 9;
    public static final int MYSQL_TYPE_DATE = 10;
    public static final int MYSQL_TYPE_TIME = 11;
    public static final int MYSQL_TYPE_DATETIME = 12;
    public static final int MYSQL_TYPE_YEAR = 13;
    public static final int MYSQL_TYPE_NEWDATE = 14;
    public static final int MYSQL_TYPE_VARCHAR = 15;
    public static final int MYSQL_TYPE_BIT = 16;
    public static final int MYSQL_TYPE_TIMESTAMP2 = 17;
    public static final int MYSQL_TYPE_DATETIME2 = 18;
    public static final int MYSQL_TYPE_TIME2 = 19;
    public static final int MYSQL_TYPE_TYPED_ARRAY = 20;
    public static final int MYSQL_TYPE_INVALID = 243;
    public static final int MYSQL_TYPE_BOOL = 244;
    public static final int MYSQL_TYPE_JSON = 245;
    public static final int MYSQL_TYPE_NEWDECIMAL = 246;
    public static final int MYSQL_TYPE_ENUM = 247;
    public static final int MYSQL_TYPE_SET = 248;
    public static final int MYSQL_TYPE_TINY_BLOB = 249;
    public static final int MYSQL_TYPE_MEDIUM_BLOB = 250;
    public static final int MYSQL_TYPE_LONG_BLOB = 251;
    public static final int MYSQL_TYPE_BLOB = 252;
    public static final int MYSQL_TYPE_VAR_STRING = 253;
    public static final int MYSQL_TYPE_STRING = 254;
    public static final int MYSQL_TYPE_GEOMETRY = 255;
    protected static final Logger logger = LoggerFactory.getLogger(LogEvent.class);
    protected final LogHeader header;

    protected String trace;

    protected Long traceServerId;

    protected LogEvent(LogHeader header) {
        this.header = header;
    }

    public static String getTypeName(final int type) {
        switch (type) {
        case START_EVENT_V3:
            return "Start_v3";
        case STOP_EVENT:
            return "Stop";
        case QUERY_EVENT:
            return "Query";
        case ROTATE_EVENT:
            return "Rotate";
        case INTVAR_EVENT:
            return "Intvar";
        case LOAD_EVENT:
            return "Load";
        case NEW_LOAD_EVENT:
            return "New_load";
        case SLAVE_EVENT:
            return "Slave";
        case CREATE_FILE_EVENT:
            return "Create_file";
        case APPEND_BLOCK_EVENT:
            return "Append_block";
        case DELETE_FILE_EVENT:
            return "Delete_file";
        case EXEC_LOAD_EVENT:
            return "Exec_load";
        case RAND_EVENT:
            return "RAND";
        case XID_EVENT:
            return "Xid";
        case USER_VAR_EVENT:
            return "User var";
        case FORMAT_DESCRIPTION_EVENT:
            return "Format_desc";
        case TABLE_MAP_EVENT:
            return "Table_map";
        case PRE_GA_WRITE_ROWS_EVENT:
            return "Write_rows_event_old";
        case PRE_GA_UPDATE_ROWS_EVENT:
            return "Update_rows_event_old";
        case PRE_GA_DELETE_ROWS_EVENT:
            return "Delete_rows_event_old";
        case WRITE_ROWS_EVENT_V1:
            return "Write_rows_v1";
        case UPDATE_ROWS_EVENT_V1:
            return "Update_rows_v1";
        case DELETE_ROWS_EVENT_V1:
            return "Delete_rows_v1";
        case BEGIN_LOAD_QUERY_EVENT:
            return "Begin_load_query";
        case EXECUTE_LOAD_QUERY_EVENT:
            return "Execute_load_query";
        case INCIDENT_EVENT:
            return "Incident";
        case HEARTBEAT_LOG_EVENT:
            return "Heartbeat";
        case IGNORABLE_LOG_EVENT:
            return "Ignorable";
        case ROWS_QUERY_LOG_EVENT:
            return "Rows_query";
        case WRITE_ROWS_EVENT:
            return "Write_rows";
        case UPDATE_ROWS_EVENT:
            return "Update_rows";
        case DELETE_ROWS_EVENT:
            return "Delete_rows";
        case GTID_LOG_EVENT:
            return "Gtid";
        case ANONYMOUS_GTID_LOG_EVENT:
            return "Anonymous_Gtid";
        case PREVIOUS_GTIDS_LOG_EVENT:
            return "Previous_gtids";
        case SEQUENCE_EVENT:
            return "Sequence";
        case GCN_EVENT:
            return "Gcn";
        case XA_PREPARE_LOG_EVENT:
            return "XA_Prepare";
        default:
            return "Unknown"; /* impossible */
        }
    }

    /**
     * Return event header.
     */
    public final LogHeader getHeader() {
        return header;
    }

    /**
     * The total size of this event, in bytes. In other words, this is the sum of the sizes of Common-Header,
     * Post-Header, and Body.
     */
    public final int getEventLen() {
        return header.getEventLen();
    }

    /**
     * Server ID of the server that created the event.
     */
    public final long getServerId() {
        return header.getServerId();
    }

    /**
     * Server ID of the server that created the event.
     */
    public final void setServerId(long serverId) {
        header.setServerId(serverId);
    }

    /**
     * The position of the next event in the master binary log, in bytes from the beginning of the file. In a binlog
     * that is not a relay log, this is just the position of the next event, in bytes from the beginning of the file. In
     * a relay log, this is the position of the next event in the master's binlog.
     */
    public final long getLogPos() {
        return header.getLogPos();
    }

    /**
     * The time when the query started, in seconds since 1970.
     */
    public final long getWhen() {
        return header.getWhen();
    }

    public final byte[] toBytes() {
        return header.getDataBuffer();
    }

    public final FormatDescriptionLogEvent getDescriptionEvent() {
        return this.header.getFormatDescriptionLogEvent();
    }

    public String getTrace() {
        return trace;
    }

    public void setTrace(String trace) {
        this.trace = trace;
    }

    public String info() {
        return getTypeName(getHeader().getType());
    }

    public String getCommitLogInfo() {
        return null;
    }

    public Long getTraceServerId() {
        return traceServerId;
    }

    public void setTraceServerId(Long traceServerId) {
        this.traceServerId = traceServerId;
    }
}
